#include "aes_tcp.h"
#include "QObject"

enum DOWN_TYPE{

    ST_PACK = 0,
    ST_FILE
};

void aes_tcp::lus_set_order(qint8 order)
{
    byte_order = order;
}
void aes_tcp::aes_tcp_close(void)
{

    msg_tcp_socket->disconnectFromHost();

}

void aes_tcp::aes_tcp_init(void)
{
    /*网络初始化*/
    msg_tcp_socket = new QTcpSocket();
    big_tcp_socket = new QTcpSocket();
    f_tcpSocket	   = new QTcpSocket();

    big_blockSize = 0;
    msg_blockSize = 0;
    bytesReceived = 0;

    if(server_ip.isEmpty())
        server_ip = "beijg.3322.org";

    if(msg_port == 0)
        msg_port = 9527;

    if(big_port == 0)
        big_port = 9526;

    fix_id.clear();
    to_addr.clear();

    QFileInfo f_i("fix_id");
    if(!f_i.isFile()){

        QDateTime time = QDateTime::currentDateTime();
        QString id = time.toString("yyyy.MM.dd-hh:mm:ss-");
        qsrand(time.time().msec()+time.time().second()*1000);

        QString rand = QString::number(qrand()%100);
        id += rand;

        QByteArray b_id = id.toLatin1().toBase64();

        QFile file("fix_id");
        file.open(QIODevice::WriteOnly);
        file.write(b_id);
        file.close();

        if(debug_switch)
            qDebug()<<"creat fix id "<<b_id;

    }

    QFile file("fix_id");
    file.open(QIODevice::ReadOnly);
    fix_id=file.readAll();
    file.close();

    connect(msg_tcp_socket,SIGNAL(error(QAbstractSocket::SocketError)),
             this,SLOT(displayError(QAbstractSocket::SocketError)));
    connect(big_tcp_socket,SIGNAL(error(QAbstractSocket::SocketError)),
             this,SLOT(displayError(QAbstractSocket::SocketError)));

    /*aes init*/
    UCHAR *p = aes_key;
    aes->InitializePrivateKey(16, p); //进行初始化

    /*掉线自动重连定时器*/
    timer_connect = new QTimer(this);
    connect( timer_connect, SIGNAL( timeout() ), this, SLOT( timer_auto_connect() ) );
    timer_connect->stop();

    /*心跳*/
    timer_heart = new QTimer(this);
    connect( timer_heart, SIGNAL( timeout() ), this, SLOT( timer_heart_keep()) );
    timer_heart->stop();

    /*缓冲init*/
    tcp_big_data_cache = (char *)malloc(sizeof (char)*1024*1024);
    tcp_msg_data_cache = (char *)malloc(sizeof (char)*1024*50);

    /*连接到服务器*/
    newConnect();

}

aes_tcp::~aes_tcp()
 {

    free(tcp_big_data_cache);
    free(tcp_msg_data_cache);

 }
void aes_tcp::timer_heart_keep(void)
{

    QByteArray cmd = "<lus_cmd body=\"heart\"  paral=\"5\" />";

    if(debug_switch)
        qDebug()<<"send heart!";

    sendMessage(cmd, ENCRYPT_YES);

}

void aes_tcp::displayError(QAbstractSocket::SocketError)
{
    qDebug() << msg_tcp_socket->errorString(); //输出错误信息
}

void aes_tcp::newConnect()
{
    msg_blockSize = 0; //初始化其为0

    msg_tcp_socket->abort(); //取消已有的连接
    big_tcp_socket->abort();

    /*连接到主机，这里从界面获取主机地址和端口号*/
     msg_tcp_socket->connectToHost(server_ip, msg_port);
   //  big_tcp_socket->connectToHost(server_ip, big_port);

     connect(msg_tcp_socket,SIGNAL(readyRead()),this,SLOT(readMessage_msg()));
     connect(big_tcp_socket,SIGNAL(readyRead()),this,SLOT(readMessage_big()));

     /*自动重连处理*/
     connect(msg_tcp_socket,SIGNAL(connected()),this,SLOT(disable_reconnect()));
     connect(msg_tcp_socket,SIGNAL(disconnected()),this,SLOT(enable_reconnect()));
}

/*新建文件传输连接*/
void aes_tcp::newFileConnect()
{

    /*等待上一个传输完成*/
/*
    while(down_status==DOWN_ING){
        QEventLoop eventloop;
        QTimer::singleShot(1000, &eventloop, SLOT(quit()));
        eventloop.exec();
    }
*/
    if(debug_switch)
        qDebug()<<"connect to file tcp";

    f_tcpSocket->abort(); //取消已有的连接
    //连接到主机，这里从界面获取主机地址和端口号
     f_tcpSocket->connectToHost(server_ip, 9528);
     connect(f_tcpSocket,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));
     connect(f_tcpSocket,SIGNAL(connected()),this,SLOT(f_connected()));
    down_status = DOWN_ING;
}

void aes_tcp::f_connected(void)
{
    qDebug()<<"file socket connected";

    QString cmd = QString("<%1,%2>").arg("id").arg(fix_id);
    qDebug()<<cmd;
    //f_tcpSocket->write(cmd.toLatin1());

    do_send_msg(f_tcpSocket, cmd.toLatin1(), ENCRYPT_YES);
}
/*接收文件数据*/
void aes_tcp::updateServerProgress()
{

   QDataStream in(f_tcpSocket);
   in.setVersion(QDataStream::Qt_4_6);

   if(bytesReceived <= sizeof(qint64)*3)
   {
       qDebug()<< "head info";
       //如果接收到的数据小于16个字节，那么是刚开始接收数据，我们保存到//来的头文件信息
       if((f_tcpSocket->bytesAvailable() >= sizeof(qint64)*3)
           && (fileNameSize == 0))
       { //接收数据总大小信息和文件名大小信息
           in >>file_type>> totalBytes >> fileNameSize;

           if(debug_switch)
               qDebug()<< file_type <<" "<< totalBytes<< " "<<fileNameSize;

           bytesReceived += sizeof(qint64) * 3;
       }
       if((f_tcpSocket->bytesAvailable() >= fileNameSize)
           && (fileNameSize != 0))
       {  //接收文件名，并建立文件
           in >> fileName;
           //ui->serverStatusLabel->setText(tr("recive file: %1 ...").arg(fileName));

           if(debug_switch)
                qDebug() << tr("recive file %1 ...").arg(fileName);

           bytesReceived += fileNameSize;
           localFile= new QFile("./"+fileName);
           if(!localFile->open(QFile::WriteOnly))
           {
                qDebug() << "open file error!";
                return;
           }
       }
       else return;
   }



   if(bytesReceived < totalBytes)
   {  //如果接收的数据小于总数据，那么写入文件
      bytesReceived += f_tcpSocket->bytesAvailable();

      qDebug()<<"recive: "<<bytesReceived;

      inBlock= f_tcpSocket->readAll();
      localFile->write(inBlock);
      inBlock.resize(0);
   }
//更新进度条

   if(bytesReceived == totalBytes )
   { //接收数据完成时
       bytesReceived = 0;
       fileNameSize = 0;
       totalBytes = 0;

        f_tcpSocket->close();
        localFile->close();

        qDebug()<<"recive file ok";

        recive_file_finally();

   }
}

/*文件接收完成后续处理*/
void aes_tcp::recive_file_finally(void)
{

    /*进行MD5校验*/
    if(!s_file_md5.isEmpty())
    {

        QString n_md5 = get_file_md5("./" + fileName);

        if(n_md5 == s_file_md5){

            if(debug_switch)
                qDebug() << "file md5 check ok";


        }else{

             qDebug() << "file md5 check fail";

             return;
        }


    }

    if(file_type == ST_PACK)
            emit recive_lus_pack(fileName);
    down_status = DOWN_OK;

}


QString aes_tcp::get_file_md5(QString str_path)
 {


     QFile theFile(str_path);
     theFile.open(QIODevice::ReadOnly);
     QByteArray ba = QCryptographicHash::hash(theFile.readAll(), QCryptographicHash::Md5);
     theFile.close();
    return ba.toHex().constData();

 }


/*自动重连*/
void aes_tcp::enable_reconnect(void)
{
    //timer_bdinfo->stop();
    timer_connect->start(4000); //10s
    timer_heart->stop();

    emit aes_tcp_disconnected();
}

void aes_tcp::disable_reconnect(void)
{

   //timer_bdinfo->start(10000);
   timer_connect->stop();
   timer_heart->start(2000);

}


/*定时器自动调用此函数*/
void aes_tcp::timer_auto_connect(void)
{

    msg_blockSize = 0; //初始化其为0
    big_blockSize = 0;

    msg_tcp_socket->abort(); //取消已有的连接
    big_tcp_socket->abort();

    //连接到主机，这里从界面获取主机地址和端口号
    msg_tcp_socket->connectToHost(server_ip,  msg_port);
    big_tcp_socket->connectToHost(server_ip, big_port);
}

void aes_tcp::lus_send_user_msg(QString type, QString to_addr, QString body)
{

    QString cmd = "<lus_user_msg type=\"@1\" to_addr=\"@2\" body=\"@3\" />";

    if(type.isEmpty()){

        qDebug()<<"lus_send_user_msg: type is null";
        return;
    }

    cmd.replace("@1", type);

     if(body.isEmpty()){

        qDebug()<<"lus_send_user_msg: body is null";
        return;
    }

     cmd.replace("@3", body);


     if(to_addr.isEmpty())
        qDebug()<<"lus_send_user_msg: to addr is null";

     cmd.replace("@2", to_addr);

     sendMessage(cmd.toLatin1(), ENCRYPT_YES);

}
void aes_tcp::lus_send_raw_msg(QByteArray b_data, quint8 encrypt_c)
{

    sendMessage(b_data, encrypt_c);

}

void aes_tcp::do_send_msg(QTcpSocket *socket,QByteArray b_data, quint8 encrypt_c)
{

 if((socket->state() == QAbstractSocket::ConnectedState) ||
                (big_tcp_socket->state() == QAbstractSocket::ConnectedState)){

     if(!to_addr.isEmpty() &&(b_data.indexOf("to_addr") == -1)){

         QByteArray addr = " to_addr=\"@@\" ";

         addr.replace("@@", to_addr.toLatin1());
         b_data.insert(b_data.size()-2, addr);
     }

    //用于暂存我们要发送的数据
    QByteArray block;
    //使用数据流写入数据
    QDataStream out(&block,QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_6);
    if(byte_order == LITTLE){
        out.setByteOrder(QDataStream::LittleEndian);
    }
    if(encrypt_c == ENCRYPT_YES){

        quint16 en_len = 0;
        char *c_data;
        c_data = b_data.data();

        DWORD size = b_data.size();
        char enc_data[1024] = {0};

        en_len = aes->OnAesEncrypt((LPVOID)c_data, (DWORD)size, (LPVOID)enc_data); //进行加密

        //打印加密信息
             //for(int i=0; i<32; i++)qDebug("# %x", enc_data[i]);

        if(debug_switch)
                qDebug() << "en_len" << en_len <<"b_data size "<< b_data.size();

        //设置数据流的版本，客户端和服务器端使用的版本要相同

        out<<(quint32) 0;
        out<<(quint8) 0x12;
        out.writeRawData(enc_data, sizeof(char)* en_len);       //原始写！！

        out.device()->seek(0);
        out<<(quint32)((en_len+sizeof(quint32)+sizeof(quint8)));

    }else{

        out<<(quint32) 0;
        out<<(quint8) 0x7;
        out.writeRawData(b_data, b_data.size());       //原始写！！
        out.device()->seek(0);
        out<<(quint32)(b_data.size()+sizeof(quint32)+sizeof (quint8));

    }

    socket->write(block);
    socket->flush();

 }else{

     qDebug()<<"socket status error!";
 }




}
void aes_tcp::sendMessage(QByteArray b_data, quint8 encrypt_c)
{
    do_send_msg(msg_tcp_socket, b_data, encrypt_c);
}


void aes_tcp::readMessage_msg()
{

    QDataStream in(msg_tcp_socket);

    quint8 encrypt_flag = 1;

    //设置数据流版本，这里要和服务器端相同
    in.setVersion(QDataStream::Qt_5_6);
    if(byte_order == LITTLE){
        in.setByteOrder(QDataStream::LittleEndian);
    }
    if(msg_blockSize==0) //如果是刚开始接收数据,新tcp帧
    {
       //判断接收的数据是否有两字节，也就是文件的大小信息
       //如果有则保存到msg_blockSize变量中，没有则返回，继续接收数据
       if(msg_tcp_socket->bytesAvailable() < (int)(sizeof(quint32)+sizeof (quint8))) {

           //ui->textBrowser->append("no data");
           return;
       }
           in >> msg_blockSize;		//读取数据长度
           in >> encrypt_flag;		//读取加密位

           /*非法数据格式，jump*/
           if((encrypt_flag != ENCRYPT_NO ) && (encrypt_flag != ENCRYPT_YES)){
                msg_blockSize = 0;
                qDebug()<<"check format error";
                return;
           }
    }

    if(msg_tcp_socket->bytesAvailable() < (msg_blockSize-sizeof (quint32)-sizeof (quint8))) return;
    //如果没有得到全部的数据，则返回，继续接收数据

    int real_data_len = msg_blockSize-sizeof (quint32)-sizeof (quint8);

    if(real_data_len > 1024*50){
        qDebug()<<"too big data to cache";
        return;
    }
    in.readRawData(tcp_msg_data_cache, real_data_len);    //原始读取！

    QByteArray byte_data;
    QDataStream b_in(&byte_data, QIODevice::WriteOnly);

    if(encrypt_flag == ENCRYPT_YES){
        //将接收到的数据存放到变量中
        if(real_data_len > 1024){
            qDebug()<<"too big to uncrypt";
            return;
        }
         char jiemi[1024] = {0};

         int un_size = 0;
            //解密
         un_size = aes->OnAesUncrypt((LPVOID)tcp_msg_data_cache, (DWORD)sizeof(char)* real_data_len,(LPVOID)jiemi); //进行解密

         if(un_size == 0){

                qDebug()<<"uncrypt error";
                return;
            }

         b_in.writeRawData(jiemi, un_size);

    }else if(encrypt_flag == ENCRYPT_NO){

        b_in.writeRawData(tcp_big_data_cache, real_data_len);

    }

    emit read_msg(byte_data);

    default_do_cmd(byte_data);

    msg_blockSize = 0;
    if(msg_tcp_socket->bytesAvailable()>10)readMessage_msg();

}

void aes_tcp::readMessage_big()
{
    QDataStream in(big_tcp_socket);

    quint8 encrypt_flag = 1;

    //设置数据流版本，这里要和服务器端相同
    in.setVersion(QDataStream::Qt_5_6);

    if(big_blockSize==0) //如果是刚开始接收数据
    {
       //判断接收的数据是否有两字节，也就是文件的大小信息
       //如果有则保存到msg_blockSize变量中，没有则返回，继续接收数据
       if(big_tcp_socket->bytesAvailable() < (qint64)(sizeof(quint32)+sizeof (quint8))) {

           return;
       }
           in >> big_blockSize;
           in >> encrypt_flag;

           /*非法数据格式，jump*/
           if((encrypt_flag != ENCRYPT_NO ) && (encrypt_flag != ENCRYPT_YES)){

               big_blockSize = 0;
               qDebug()<<"check format error";

                return;
           }
    }


    if(big_tcp_socket->bytesAvailable() < (big_blockSize-sizeof (quint32)-sizeof (quint8))) return;
    //如果没有得到全部的数据，则返回，继续接收数据

    int real_data_len = big_blockSize-sizeof (quint32)-sizeof (quint8);

    if(real_data_len > 1024*1024){

        qDebug()<<"too big data to big cache";
        return;
    }

    if(encrypt_flag == ENCRYPT_YES){

        qDebug()<<"big msg is not support encrypt";
        return;
    }

    in.readRawData(tcp_big_data_cache, real_data_len);    //原始读取！

    QByteArray byte_data;
    QDataStream b_in(&byte_data, QIODevice::WriteOnly);

    b_in.writeRawData(tcp_big_data_cache, real_data_len);

    emit read_msg(byte_data);

    big_blockSize = 0;

    default_do_big_cmd(byte_data);

}

void aes_tcp::default_do_big_cmd(QString cmd)
{
   QDomDocument xml;
   xml.setContent(cmd);
   QDomElement rootnode = xml.documentElement();

    if(rootnode.tagName() == "lus_user_msg" || rootnode.tagName() == "user_msg"){
       if(rootnode.attributeNode("type").value() == "tank_image"){

           QString image_base64 = rootnode.attributeNode("body").value();

            QByteArray Ret_bytearray;
            Ret_bytearray =QByteArray::fromBase64(image_base64.toLatin1());
            QImage image;
            image.loadFromData(Ret_bytearray);

            emit image_ready(&image);

       }

        return;
   }

}

void aes_tcp::update_fix_id()
{

    if(!fix_id.isEmpty()){

        QString bd_info = "<bd_info fix_id=\"##\" version=\"0\" />";
        bd_info.replace("##", fix_id);
        sendMessage(bd_info.toLatin1(), ENCRYPT_YES);

    }


}

void aes_tcp::default_do_cmd(QString cmd)
{
   QDomDocument xml;
   xml.setContent(cmd);

   QDomElement rootnode = xml.documentElement();

   if(rootnode.tagName() == "init_msg"){

       from_server_id = rootnode.attributeNode("id").value();

       if(debug_switch)
           qDebug()<<from_server_id;

        /*只有收到init msg才发射连接成功*/
        emit aes_tcp_connected();

       if(!fix_id.isEmpty()){

           QString bd_info = "<bd_info id=\"@@\" fix_id=\"##\" version=\"0\" />";
           bd_info.replace("##", fix_id);
           bd_info.replace("@@", from_server_id);
           sendMessage(bd_info.toLatin1(), ENCRYPT_YES);

           if(debug_switch)
                qDebug()<<bd_info;

       }


       return;
   }

   if(rootnode.tagName()=="lus_cmd"){

       if(rootnode.attributeNode("body").value() == "ruin"){
          timer_connect->deleteLater();
          exit(0);
       }

       if(rootnode.attributeNode("body").value() == "send_file"){

           //if(debug_switch)
               qDebug() << "send file";

           s_file_md5 =  rootnode.attributeNode("md5").value();
           newFileConnect();
           return;


       }
       return;
   }

    if(rootnode.tagName() == "lus_term_cmd"){

        prase_terminal_cmd(rootnode);
        return;
    }

    if(rootnode.tagName() == "send_file"){


    }

    emit read_xml_msg(rootnode);

  }

void aes_tcp::lus_down_pool_file(QString index, QString label, QString file_name)
{


   QString cmd = "<lus_requst type=\"file_down\" index=\"@1\" label=\"@2\" file_name=\"@3\" >";

   cmd.replace("@1", index);
   cmd.replace("@2", label);
   cmd.replace("@3", file_name);

   sendMessage(cmd.toLatin1(), ENCRYPT_YES);

}

 void aes_tcp::prase_terminal_cmd(QDomElement rootnode)
 {


    QString str_cmd = rootnode.attributeNode("body").value();

    QStringList arg;
    arg = str_cmd.split(" ");
    str_cmd = arg.at(0);
    arg.removeAt(0);

    QProcess process;
    process.start(str_cmd, arg); //使用free完成获取
    process.waitForFinished(500);
    QString str = process.readAll();

    process.close();

    if(!str.isEmpty()){

        QByteArray byte_xml = "<term_cmd_bak";

        byte_xml += (" body=\"" + str.toLatin1().toBase64() + "\"");
        byte_xml += " />";

        if(debug_switch)
            qDebug() <<byte_xml;

        sendMessage(byte_xml, ENCRYPT_NO);
    }

 }

